﻿#include "QtCEFHandler.h"

#include "include/base/cef_bind.h"
#include "include/base/cef_callback.h"
#include "include/cef_app.h"
#include "include/cef_parser.h"
#include "include/views/cef_browser_view.h"
#include "include/views/cef_window.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"

namespace {

QtCEFHandler* g_instance = nullptr;

// Returns a data: URI with the specified contents.
std::string GetDataURI(const std::string& data, const std::string& mime_type) {
    return "data:" + mime_type + ";base64," +
           CefURIEncode(CefBase64Encode(data.data(), data.size()), false).ToString();
}

}   // namespace

QtCEFHandler::QtCEFHandler()
    : m_is_closing(false)
    , m_can_quit(false)
    , m_window_rect{0, 0, -1, -1} {
    DCHECK(!g_instance);
    g_instance = this;
}

QtCEFHandler::~QtCEFHandler() {
    g_instance = nullptr;
}

// static
QtCEFHandler* QtCEFHandler::GetInstance() {
    return g_instance;
}

void QtCEFHandler::OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) {
    CEF_REQUIRE_UI_THREAD();
}

void QtCEFHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
    CEF_REQUIRE_UI_THREAD();

    // Add to the list of existing browsers.
    m_browser_list.push_back(browser);

    // 加载已经等待着的 URL
    if (!m_url_to_load.empty()) {
        CefString strUrl(m_url_to_load);
        browser->GetMainFrame()->LoadURL(strUrl);
        m_url_to_load.clear();
    }

    // 调整到指定大小
    if (m_window_rect.w >= 0 || m_window_rect.h >= 0) {
        HWND wnd = get_browser_window_handle();
        ::MoveWindow(wnd, m_window_rect.x, m_window_rect.y, m_window_rect.w, m_window_rect.h, true);
    }
}

bool QtCEFHandler::DoClose(CefRefPtr<CefBrowser> browser) {
    CEF_REQUIRE_UI_THREAD();

    // Closing the main window requires special handling. See the DoClose()
    // documentation in the CEF header for a detailed destription of this
    // process.
    if (m_browser_list.size() == 1) {
        // Set a flag to indicate that the window close should be allowed.
        m_is_closing = true;
    }

    // Allow the close. For windowed browsers this will result in the OS close
    // event being sent.
    return false;
}

void QtCEFHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
    CEF_REQUIRE_UI_THREAD();

    // Remove from the list of existing browsers.
    for (auto bit = m_browser_list.begin(); bit != m_browser_list.end(); ++bit) {
        if ((*bit)->IsSame(browser)) {
            m_browser_list.erase(bit);
            break;
        }
    }

    if (m_browser_list.empty()) {
        m_can_quit = true;
    }
}

void QtCEFHandler::OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
                               ErrorCode errorCode, const CefString& errorText,
                               const CefString& failedUrl) {
    CEF_REQUIRE_UI_THREAD();

    // Don't display an error for downloaded files.
    if (errorCode == ERR_ABORTED)
        return;

    // Display a load error message using a data: URI.
    std::stringstream ss;
    ss << "<html><body bgcolor=\"white\">"
          "<h2>Failed to load URL "
       << std::string(failedUrl) << " with error " << std::string(errorText) << " (" << errorCode
       << ").</h2></body></html>";

    frame->LoadURL(GetDataURI(ss.str(), "text/html"));
}

bool QtCEFHandler::OnPreKeyEvent(CefRefPtr<CefBrowser> browser, const CefKeyEvent& event,
                                 CefEventHandle os_event, bool* is_keyboard_shortcut) {
    if (event.type == KEYEVENT_RAWKEYDOWN) {
        switch (event.windows_key_code) {
        case VK_F5:   // F5 刷新页面
            browser->Reload();
            return true;
        case VK_F12:   // F12 打开浏览器开发者工具
            CefWindowInfo      windowInfo;
            CefBrowserSettings settings;
            windowInfo.SetAsPopup(NULL, "Dev Tools");
            browser->GetHost()->ShowDevTools(windowInfo, this, settings, CefPoint());
            return true;
        }
    }
    return false;
}

void QtCEFHandler::close_all_browsers(bool force_close) {
    m_can_quit = false;
    if (!CefCurrentlyOn(TID_UI)) {
        // Execute on the UI thread.
        CefPostTask(TID_UI, base::BindOnce(&QtCEFHandler::close_all_browsers, this, force_close));
        return;
    }

    if (m_browser_list.empty()) {
        m_can_quit = true;
        return;
    }

    for (auto browser : m_browser_list)
        browser->GetHost()->CloseBrowser(force_close);
}